Update to .NET 10 SDK with workload version set pinning#3514
Merged
Conversation
- Pin SDK to 10.0.103 with workload sets in global.json - Restructure TFMs: TFMBase=net6.0, TFMPrevious=net9.0, TFMCurrent=net10.0 - Remove TFMNext and all associated conditional blocks - Remove MAUI TPV workaround (dotnet/maui#24263 fixed in .NET 9+) - Update all 14 NativeAssets csproj files to TFMPrevious+TFMCurrent pattern - Update platform TPVs for .NET 9 (Previous) and .NET 10 (Current) - Replace workload install script with dotnet workload restore + Tizen - Remove DOTNET_VERSION_PREVIEW and preview SDK install step - Update pipeline variables: SDK 10.0.103, Xcode 16.2, Emscripten 3.1.56 - Update Blazor csproj with net10.0 PackageReference - Update all sample, test, utility, and cake files - Clean up NuGet.config (remove old preview feeds) - Add documentation/updating-dotnet-version.md upgrade checklist
- global.json: Use 10.0.100 with latestPatch so any 10.0.1xx SDK satisfies the constraint (CI agents have 10.0.102, not 10.0.103) - All Dockerfiles: Update from .NET 8 to .NET 10 SDK - WASM: mcr.microsoft.com/dotnet/sdk:8.0 → 10.0 - Linux/Alpine: DOTNET_SDK_VERSION=8.0 → 10.0
The workloadVersion field causes native builds (WinUI, etc.) to fail when the pre-installed SDK version doesn't match the pinned workload version. Native builds skip the SDK/workload install step but still need dotnet. Managed builds get workloads via 'dotnet workload restore' which uses the SDK's bundled manifests — no global.json pin needed.
dotnet workload restore requires a project/solution file but there is none in the repo root. Revert to explicit dotnet workload install with the specific workload IDs needed by the repo.
- Add DOTNET_WORKLOAD_VERSION variable (10.0.103) to azure-templates-variables.yml - Pass to install-dotnet-workloads.ps1 via -WorkloadVersion parameter - Script uses 'dotnet workload install --version' for reproducible workload sets - Keep global.json clean (no workloadVersion) so native builds aren't affected - Update documentation to reflect new approach
- Update DOTNET_WORKLOAD_TIZEN to 10.0.123 (latest available for 10.0.100 band) - Pass --dotnet-target-version-band 10.0.100 to Samsung's install scripts since their LatestVersionMap doesn't have .NET 10 entries yet
.NET 10 Apple workloads use Xcode 26 unified SDK versioning: - iOS/MacCatalyst/tvOS/macOS TPV: 18.0/15.0 → 26.0 SupportedOSPlatformVersion minimums bumped in .NET 9/10 workloads: - iOS/tvOS: 10.0 → 12.2 - MacCatalyst: 13.1 → 15.0 - macOS: 10.14 → 12.0
The Samsung Tizen workload must be installed before 'dotnet workload install --version' locks the SDK into workload-set mode. Reorder to install Tizen first via Samsung scripts, then official workloads with version pinning.
…or device tests - Remove --version from dotnet workload install to avoid workload-set mode which makes the SDK ignore Samsung Tizen manifests installed separately - Update WindowsSdkPackageVersion from 10.0.19041.27 to 10.0.19041.38 (required by WindowsAppSDK 1.7) - Add explicit TPVs to MauiTargetFrameworksApp* (26.0 for Apple, 36.0 for Android) to avoid resolving to default TPV 26.2 which requires Xcode 26.2 - Update build.cake device test TFMs to match TPV-qualified forms - Remove unused DOTNET_WORKLOAD_VERSION pipeline variable
- Force manifest update mode with 'dotnet workload config --update-mode manifests' before installing workloads. .NET 10 SDK auto-switches to workload-set mode which ignores Samsung Tizen manifests. - Use --skip-manifest-update to prevent official workload install from downloading a workload set and overriding manifest mode. - Install Tizen AFTER official workloads (order doesn't matter in manifest mode). - Use net9.0 TFMs for iOS/MacCatalyst device tests because net10.0 Apple SDK packs (both 26.0 and 26.2) require Xcode 26.0+ which CI doesn't have. - Add Previous TFMs to device test project so net9.0 builds are available.
Debug Tizen install: show manifest directory contents and dotnet workload list before and after official workload install to understand why Samsung.Tizen FrameworkReference is not recognized.
- Set buildExternals default to 155857 to skip native rebuilds - SkiaSharp.Tests: Add TFMPrevious so device tests can reference it from net9.0 TFMs (NU1201: not compatible) - SkiaSharp.Tests.Devices: Bump MacCatalyst min to 15.0 (workload minimum)
Tizen SDK 10.0.123 only defines KnownFrameworkReference for TPV 8.0+. TPV 7.0 is listed as supported but has no Samsung.Tizen framework ref, causing NETSDK1073 'Samsung.Tizen was not recognized'.
- Remove --skip-manifest-update from official workload install: The flag prevented downloading manifests, so workloads had nothing to install (empty 'Successfully installed workload(s) .' output). - Fix WASM AOT P/Invoke type conflict: Test used int/int signature for hb_script_get_horizontal_direction but binding uses uint/Direction. .NET 10 WASM AOT compiler flags this as a C-level type conflict.
The HarfBuzzSharp binding declares Direction as int-backed enum with uint32_t parameter. The test must match: int32_t return, uint32_t param. Previous fix incorrectly used uint32_t for both.
MAUI 10.0.0 dropped Tizen support entirely - the NuGet package has no net10.0-tizen* assemblies. Samsung Tizen 10.0.123 workload removed KnownFrameworkReference for TPV 7.0 (needed by MAUI 9.0), making net9.0-tizen7.0 MAUI builds impossible too. Non-MAUI Tizen support (NativeAssets, bindings) still works with net9.0-tizen8.0 and net10.0-tizen10.0.
- Remove Microsoft.Maui.Controls.Compatibility.Hosting from MAUI sample (namespace removed in MAUI 10.0) - Remove Tizen from MAUI sample TargetFrameworks (MAUI 10 dropped Tizen) - Update Tizen sample tizen-manifest.xml api-version to 13 (matches TPV 10.0 → API level 13 in Samsung SDK 10.0.123) - Update Apple ref package names in UpdateDocs.cake for TPV 26.0
…e sample TFMs - Block TypeScript recompilation in Blazor csproj to fix .NET 10 static web assets compression race condition (TypeScriptCompileBlocked=true) - Update VERSIONS.txt: Apple ref pack names from net8.0_17.0 to net10.0_26.0, MAUI versions from 8.0.3 to 10.0.31, tvOS ref pack name updated - Switch Apple sample apps (iOS, MacCatalyst, MAUI, Uno) to net9.0 TFMs since net10.0 Apple TFMs require Xcode 26.0+ (CI has Xcode 16.2)
With the .NET 10 SDK, bare net9.0-ios resolves to TPV 26.0 (not 18.0), which requires Xcode 26.0. Explicit TPVs (net9.0-ios18.0, net9.0-maccatalyst18.0) are needed to match the Xcode 16.2 available on CI.
iOS SDK 18.0 requires minimum SupportedOSPlatformVersion of 12.2.
The truncation-based comparison with (int)(v * pow) / pow doesn't handle negative values correctly near precision boundaries. For example, -4.9999995 truncates to -4.9999 (not -5.0), causing false test failures. This became apparent with .NET 9+ which changed Matrix4x4.CreateFromAxisAngle to go through Quaternion, producing slightly different floating-point results. Using Math.Round() is the correct approach for floating-point similarity comparison.
- Add Apple TPV 26.0 unified SDK versioning note - Add SupportedOSPlatformVersion section - Add global.json rollForward and workloadVersion warnings - Add Docker images section - Add Known Issues section (floating-point precision, Apple TPVs, MAUI breaking changes) - Add CI Troubleshooting section (artifact reuse, SDK mismatch, workload install) - Update workload pinning explanation
- nuget.org is disallowed in CI, must be removed before merging - buildExternals must be reset to 'latest' before merging - Add note about temporarily adding nuget.org during development
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Move ANDROID_AVD_HOME/EMULATOR_HOME setup from cake to YAML pipeline (fixes XDG path mismatch on Linux CI agents) - Remove nuget.org from nuget.config (was temporary workaround) - Remove manifest mode override from workload install script (use default workload version sets now that .NET 10 packages are published) - Simplify config.ini partition size code to use ANDROID_AVD_HOME env var Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Redth
reviewed
Mar 11, 2026
| displayName: 'The Build ID containing the specific native artifacts to use:' | ||
| type: string | ||
| default: 'latest' | ||
| default: '155848' |
Contributor
There was a problem hiding this comment.
Do you mean to change this to be hard coded?
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Workload-set mode resolves to 10.0.104 which requires packages (e.g. microsoft.macos.sdk.net9.0_26.2 v26.2.9000) not yet mirrored to the dnceng feeds. Manifest mode uses the SDK's bundled manifests which only reference packages available at SDK release time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Even in manifest mode, dotnet workload install tries to update manifests which resolves to packages (e.g. microsoft.ios.windows.sdk.net9.0_26.2 v26.2.9000) not available in the dnceng feeds. --skip-manifest-update forces use of the SDK's bundled manifests only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The missing packages just need mirroring to dnceng, not a workaround. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pass --version to dotnet workload install on CI to pin all agents to the same workload set. This keeps developer machines unaffected (no global.json workloadVersion) while ensuring CI builds are deterministic. - Add -WorkloadSetVersion parameter to install-dotnet-workloads.ps1 - Add DOTNET_WORKLOAD_VERSION variable in azure-templates-variables.yml - Pass the version through azure-templates-jobs-bootstrapper.yml Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
XHarness already disables the package verifier internally before installing APKs. The 'android adb' subcommand doesn't exist in the current dotnet-android tool, causing 'Unknown command' errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test creates 4 native Skia objects per iteration (bitmap, scaledBitmap, image, data) across 100 threads × 1000 iterations (400K allocations) but never disposed them, relying solely on GC finalization every 500ms. On CI agents with limited memory, the GC couldn't keep up, causing 'Unable to allocate pixels'. Add using declarations to all native objects so memory is released deterministically. The test still validates threading correctness (concurrent decode/scale/encode) while the GC background task remains as additional stress. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 4707163.
Remove the TypeScriptCompileBlocked workaround in the Blazor project and instead redirect TypeScript output to per-TFM intermediate directories using TypeScriptOutDir. This eliminates the multi-TFM race condition where parallel builds would delete/rewrite shared wwwroot/*.js files while another TFM's static web asset compression reads them. The compiled JS still goes to obj/<Config>/<TFM>/ts/ per-TFM while the checked-in wwwroot/*.js files remain the source-of-truth for static web assets. TypeScript compilation now validates the .ts source on every build instead of being blocked entirely. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The --partition-size flag alone doesn't override the system image's default userdata partition size. API 36 requires 7373 MB by default, but CI agents only have ~6.5 GB free after build artifacts download. The emulator starts in a degraded state (FATAL: not enough space) and crashes during APK install with 'device offline'. Restore the config.ini approach (disk.dataPartition.size=2048M) in addition to the --partition-size flag, and log the full config.ini contents for debugging. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pinned build ID 155848 was used during development to speed up CI iterations. Now that native builds are passing, revert to 'latest' so the pipeline always uses the most recent native artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Local testing confirmed that: - `--partition-size` acts as a floor: max(flag, system_image_default) - API 36 system images already default to 6+ GB data partitions - Setting config.ini disk.dataPartition.size is overwritten by the emulator - Both the config.ini hack and --partition-size 2048 were no-ops The INSTALL_FAILED_INSUFFICIENT_STORAGE issue was likely caused by the emulator being offline, not by partition size. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Docker images for Linux/WASM builds have SDK 10.0.201 (feature band 2). The previous pin to 10.0.100 with latestPatch only allowed 10.0.1xx, causing all Linux and WASM native builds to fail with SDK not found. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Windows ANGLE agents have 10.0.102, Linux Docker images have 10.0.201. Using 10.0.100 as the base version with latestFeature allows any 10.0.xxx feature band to satisfy the requirement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
Apr 29, 2026
Restores the original intent of MauiTargetFrameworksApp from commit 40b0f57 — app TFMs should have no TPV so they use whatever workload is installed. The TPVs were accidentally added in 58a1a73 (#3514). Windows keeps the TPV since it's a Windows SDK version, not a workload. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
Apr 30, 2026
…#3798) Fix Apple platform TFMs: use 26.0 for libraries, unversioned for apps (#3798) Fixes: #3514 (regression) The .NET 10 upgrade in #3514 set the Apple Target Platform Versions to 26.2 (matching the CI's Xcode version) instead of 26.0 (the base Xcode 26 unified SDK version). This caused library NuGet packages to ship with TFMs like `net10.0-ios26.2`, forcing consumers to install Xcode 26.2 rather than accepting any 26.x workload. The same PR also accidentally added explicit TPVs to the `MauiTargetFrameworksApp` properties, which were originally introduced in 40b0f57 with no TPV so that apps pick up whatever workload is installed on the build machine. The fix splits the versioning strategy: * Libraries pin to the `.0` TPV (`net10.0-ios26.0`) for maximum compatibility — consumers need only the base workload. * Apps (test devices, samples) use unversioned TFMs (`net10.0-ios`) so they build against whatever Xcode is on the CI agent or dev machine, avoiding SDK/Xcode version mismatches. Files changed: * `source/SkiaSharp.Build.props` — Apple TPVs 26.2 → 26.0; `MauiTargetFrameworksApp` restored to unversioned * `build.cake` — test task TFMs to unversioned * `scripts/VERSIONS.txt` — ref pack names and versions to 26.0 * `scripts/cake/UpdateDocs.cake` — ref pack package names to 26.0 Verified: all 15 Apple-platform NuGet packages contain `26.0` TFMs. iOS and Mac Catalyst CI tests pass with Xcode 26.2 installed. Co-authored-by: Matthew Leibowitz <mattleibow@live.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mattleibow
added a commit
that referenced
this pull request
May 29, 2026
Fix WinUI Projection DLL not resolved for .NET 9 consumers on Windows (#4084) Fixes: #4082 When TFMCurrent moved from net8.0 to net10.0 (in #3514), the `NativeAssetPackageFile` entries in `SkiaSharp.NativeAssets.WinUI.csproj` placed `SkiaSharp.Views.WinUI.Native.Projection.dll` only into: runtimes/win-{arch}/lib/net10.0-windows10.0.19041.0/ NuGet's TFM resolution algorithm won't serve a net10.0 runtime asset to a net9.0 consumer — higher TFMs are not backward-compatible. .NET 9 apps hit a `FileNotFoundException` at startup: Could not load file or assembly 'SkiaSharp.Views.WinUI.Native.Projection, ...' In v3.119.2 (TFMCurrent=net8.0) this worked because the DLL landed in `lib/net8.0-windows.../`, which both net8 and net9 consumers could resolve. Include the Projection DLL in both `$(WindowsTargetFrameworksCurrent)` and `$(WindowsTargetFrameworksPrevious)` folders so NuGet serves it regardless of whether the consuming app targets net9 or net10. The DLL itself is built targeting net9.0-windows and is binary-compatible with both. Size overhead is ~25 KB × 3 RIDs — negligible against the ~450 MB package. Co-authored-by: Matthew Leibowitz <mattleibow@live.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upgrade SkiaSharp from .NET 8 SDK to .NET 10 SDK. Updates the build system, CI pipelines, TFM architecture, workload management, samples, and test infrastructure.
89 files changed, 606 insertions, 367 deletions
TFM Architecture Change
Restructured from
Previous/Current/NexttoBase/Previous/Current:TFMBasenet6.0TFMPreviousnet6.0net9.0TFMCurrentnet8.0net10.0TFMNextPlatform TFMs only support two versions (current + previous), so
Nextwas eliminated.Basewas added for class library TFMs (net6.0) that aren't tied to platform workloads.Third-Party Platform Versions (TPVs)
Workload Management — Workload Version Sets
Replaced ad-hoc manifest mode and
--skip-manifest-updateworkarounds with deterministic workload version set pinning.Key changes in
scripts/install-dotnet-workloads.ps1:WorkloadSetVersionis now a mandatory parameterdotnet workload install --version <version>for deterministic resolutionDOTNET_WORKLOAD_VERSIONpipeline variable (10.0.104) centralizes the versionglobal.jsonworkloadVersion— only CI is pinned, developers use whatever they have installedSDK Pinning
global.jsonupdated:8.0.100→10.0.100rollForward:latestMinor→latestFeaturelatestFeatureallows any10.0.xxxfeature band (e.g.,10.0.102,10.0.201), which is necessary because CI environments have different SDK versions (Docker images use10.0.201, Windows agents install10.0.103)CI Pipeline Changes
SDK & Tooling
8.0.304→10.0.103(viaDOTNET_VERSIONvariable)15.4→26.2(managed builds),15.4→16.2(native builds)dotnet/sdk:8.0→dotnet/sdk:10.09.0.0-prerelease.24317.3→11.0.0-prerelease.26107.13.1.34→3.1.5634→3617.2→26.2macos-14→macos-15Removed / Cleaned Up
DOTNET_VERSION_PREVIEWvariable and conditional preview SDK install stepDOTNET_WORKLOAD_SOURCEmanifest URL — replaced by workload version setsinstallPreviewVsparameter and the conditional Visual Studio install blockdotnet6,dotnet7,dotnet8,test-device-runners) fromnuget.config--skip-manifest-update,dotnet workload config --update-mode manifests)Android Emulator (Linux)
ANDROID_AVD_HOMEandANDROID_EMULATOR_HOMEvia##vso[task.setvariable]in YAML to fix XDG path mismatch (~/.config/.android/avd/vs~/.android/avd/)Package Updates
Build & Test
xunit/xunit.extensibility.core/xunit.runner.utility: 2.4.2 → 2.9.3xunit.runner.visualstudio: 2.4.5 → 2.8.2Microsoft.TypeScript.MSBuild: 5.7.1 → 5.9.3DeviceRunners.*: 0.1.0-preview.1 → 0.1.0-preview.6Uno.Sdk: 5.2.175 → 6.5.31TypeScript Build Fix (Blazor)
Replaced
TypeScriptCompileBlockedwith per-TFMTypeScriptOutDir:Root cause: multi-TFM parallel builds would delete/rewrite shared
wwwroot/*.jsfiles viaTypeScriptDeleteOutputFromOtherConfigswhile another TFM'sStaticWebAssets.Compressionwas reading them. Redirecting TypeScript output to a per-TFM intermediate directory +<Content>items resolves the race.Workarounds Required for .NET 10
TypeScriptOutDirper-TFM — Blazor multi-TFM build race condition (see above)SKColorSpaceStructsTestprecision —Assert.Equal(expected, actual, 5)→ 4 decimal places for float rounding differencesuint→intto match binding declaration18.xto26.x(aligned with Xcode version numbers in Xcode 26)Samples
Platforms/Tizen/folder (Tizen MAUI no longer supported), removedUseMauiCompatibilitySkiaHostBuilder→UnoPlatformHostBuilder, added solution filtersSupportedOSPlatformVersionminimums to match new TPVsDocumentation
documentation/updating-dotnet-version.md— step-by-step guide for future .NET version upgradesKnown Issues
SKBitmapThreadingTest.ImageScalingMultipleThreadsTest(100, 1000)— pre-existing flaky OOM on CI (also fails onmain). The test intentionally avoids disposal to stress-test GC finalization across 100 threads × 1000 iterations, which exhausts memory on constrained CI agents.